/*
 * Copyright (c) 2024 Allwinner Device Co., Ltd.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "audio_sapm.h"
#include "audio_platform_base.h"
#include "audio_stream_dispatch.h"
#include "t527_codec_impl.h"
#include "audio_driver_log.h"
#include "audio_codec_base.h"

#include "t527_codec_ops.h"

#define HDF_LOG_TAG HDF_AUDIO_DRIVER

static const struct AudioSapmRoute g_audioRoutes[] = {
    { "SPKL", NULL, "SPKL PGA"},
    { "SPKR", NULL, "SPKR PGA"},
    { "SPKL PGA", "Lineout1 Switch", "DACL"},
    { "SPKR PGA", "Lineout2 Switch", "DACR"},

    { "HPL", NULL, "HPL PGA"},
    { "HPR", NULL, "HPR PGA"},
    { "HPL PGA", "Headphone Playback Switch", "DACL"},
    { "HPR PGA", "Headphone Playback Switch", "DACR"},

    { "ADC1", NULL, "LPGA"},
    { "LPGA", "ADC1 Switch", "MIC1"},

    { "ADC2", NULL, "RPGA"},
    { "RPGA", "ADC2 Switch", "MIC2"},

    { "ADC3", NULL, "MIC"},
    { "MIC", "ADC3 Switch", "MIC3"},
};

int32_t T527CodecDeviceInit(struct AudioCard *audioCard, const struct CodecDevice *codec)
{
    if (audioCard == NULL || codec == NULL || codec->devData == NULL ||
        codec->devData->sapmComponents == NULL || codec->devData->controls == NULL) {
        AUDIO_DRIVER_LOG_ERR("input para is NULL.");
        return HDF_ERR_INVALID_OBJECT;
    }

    if (CodecSetCtlFunc(codec->devData, AUDIO_CONTROL_MIXER, T527CodecAiaoGetCtrlOps,
        T527CodecAiaoSetCtrlOps) != HDF_SUCCESS) {
        AUDIO_DRIVER_LOG_ERR("AudioCodecSetCtlFunc failed.");
        return HDF_FAILURE;
    }

    if (T527CodecHalSysInit(codec->devData) != HDF_SUCCESS) {
        AUDIO_DRIVER_LOG_ERR("T527CodecHalSysInit failed.");
        return HDF_FAILURE;
    }

    if (T527CodecRegDefaultInit(codec->devData->regCfgGroup) != HDF_SUCCESS) {
        AUDIO_DRIVER_LOG_ERR("T527CodecRegDefaultInit failed.");
        return HDF_FAILURE;
    }

    if (AudioAddControls(audioCard, codec->devData->controls, codec->devData->numControls) != HDF_SUCCESS) {
        AUDIO_DRIVER_LOG_ERR("add controls failed.");
        return HDF_FAILURE;
    }

    if (AudioSapmNewComponents(audioCard, codec->devData->sapmComponents,
        codec->devData->numSapmComponent) != HDF_SUCCESS) {
        AUDIO_DRIVER_LOG_ERR("new components failed.");
        return HDF_FAILURE;
    }

    if (AudioSapmAddRoutes(audioCard, g_audioRoutes, HDF_ARRAY_SIZE(g_audioRoutes)) != HDF_SUCCESS) {
        AUDIO_DRIVER_LOG_ERR("add route failed.");
        return HDF_FAILURE;
    }

    if (AudioSapmNewControls(audioCard) != HDF_SUCCESS) {
        AUDIO_DRIVER_LOG_ERR("add sapm controls failed.");
        return HDF_FAILURE;
    }

    if (AudioSapmSleep(audioCard) != HDF_SUCCESS) {
        AUDIO_DRIVER_LOG_ERR("add sapm sleep modular failed.");
        return HDF_FAILURE;
    }

    AUDIO_DRIVER_LOG_DEBUG("success.");
    return HDF_SUCCESS;
}

int32_t T527CodecDaiDeviceInit(struct AudioCard *card, const struct DaiDevice *device)
{
    if (device == NULL || device->devDaiName == NULL) {
        AUDIO_DRIVER_LOG_ERR("input para is NULL.");
        return HDF_FAILURE;
    }

    AUDIO_DRIVER_LOG_DEBUG("codec dai device name: %s\n", device->devDaiName);
    (void)card;
    return HDF_SUCCESS;
}

int32_t T527CodecDaiStartup(const struct AudioCard *card, const struct DaiDevice *device)
{
    AUDIO_DRIVER_LOG_DEBUG("");

    (void)card;
    (void)device;

    AUDIO_DRIVER_LOG_DEBUG("success.");
    return HDF_SUCCESS;
}

int32_t T527CodecDaiHwParams(const struct AudioCard *card, const struct AudioPcmHwParams *param)
{
    int ret;
    AUDIO_DRIVER_LOG_DEBUG("");

    if (param == NULL || param->cardServiceName == NULL || card == NULL ||
        card->rtd == NULL || card->rtd->codecDai == NULL || card->rtd->codecDai->devData == NULL ||
        card->rtd->codecDai->devData->regCfgGroup == NULL) {
        AUDIO_DRIVER_LOG_ERR("input para is NULL.");
        return HDF_FAILURE;
    }

    ret = T527CodecImplHwParams(card->rtd->codecDai->devData->regCfgGroup, param->streamType,
                param->format, param->channels, param->rate);
    if (ret != HDF_SUCCESS) {
        return HDF_FAILURE;
    }

    AUDIO_DRIVER_LOG_DEBUG("success.");
    return HDF_SUCCESS;
}

int32_t T527CodecDaiTrigger(const struct AudioCard *card, int cmd, const struct DaiDevice *device)
{
    int ret;

    AUDIO_DRIVER_LOG_DEBUG(" cmd -> %d", cmd);

    (void)device;
    if (card == NULL || card->rtd == NULL || card->rtd->codecDai == NULL ||
        card->rtd->codecDai->devData == NULL || card->rtd->codecDai->devData->regCfgGroup == NULL) {
        AUDIO_DRIVER_LOG_ERR("input para is NULL.");
        return HDF_FAILURE;
    }

    switch (cmd) {
        case AUDIO_DRV_PCM_IOCTL_RENDER_START:
        case AUDIO_DRV_PCM_IOCTL_RENDER_RESUME:
            ret = T527CodecImplTrigger(card->rtd->codecDai->devData->regCfgGroup, 
                                        AUDIO_RENDER_STREAM, true);
            if (ret != HDF_SUCCESS) {
                return HDF_FAILURE;
            }
            break;
        case AUDIO_DRV_PCM_IOCTL_RENDER_STOP:
        case AUDIO_DRV_PCM_IOCTL_RENDER_PAUSE:
            ret = T527CodecImplTrigger(card->rtd->codecDai->devData->regCfgGroup, 
                                        AUDIO_RENDER_STREAM, false);
            if (ret != HDF_SUCCESS) {
                return HDF_FAILURE;
            }
            break;
        case AUDIO_DRV_PCM_IOCTL_CAPTURE_START:
        case AUDIO_DRV_PCM_IOCTL_CAPTURE_RESUME:
            ret = T527CodecImplTrigger(card->rtd->codecDai->devData->regCfgGroup, 
                                        AUDIO_CAPTURE_STREAM, true);
            if (ret != HDF_SUCCESS) {
                return HDF_FAILURE;
            }
            break;
        case AUDIO_DRV_PCM_IOCTL_CAPTURE_STOP:
        case AUDIO_DRV_PCM_IOCTL_CAPTURE_PAUSE:
            ret = T527CodecImplTrigger(card->rtd->codecDai->devData->regCfgGroup, 
                                        AUDIO_CAPTURE_STREAM, false);
            if (ret != HDF_SUCCESS) {
                return HDF_FAILURE;
            }
            break;
        default:
            break;
    }

    AUDIO_DRIVER_LOG_DEBUG("success.");
    return HDF_SUCCESS;
}

int32_t T527CodecDaiShutdown(const struct AudioCard *audioCard, const struct DaiDevice *dai)
{
    int ret;
    AUDIO_DRIVER_LOG_DEBUG("");

    (void)audioCard;
    (void)dai;

    ret = T527CodecImplShutdown();
    if (ret != HDF_SUCCESS) {
        return HDF_FAILURE;
    }

    AUDIO_DRIVER_LOG_DEBUG("success.");
    return HDF_SUCCESS;
}

int32_t T527CodecDeviceReadReg(const struct CodecDevice *codec, uint32_t reg, uint32_t *value)
{
    (void)codec;

    if (value == NULL) {
        AUDIO_DRIVER_LOG_ERR("param value is null.");
        return HDF_FAILURE;
    }

    T527CodecImplRegmapRead(reg, value);
    return HDF_SUCCESS;
}

int32_t T527CodecDeviceWriteReg(const struct CodecDevice *codec, uint32_t reg, uint32_t value)
{
    (void)codec;

    T527CodecImplRegmapWrite(reg, value);
    return HDF_SUCCESS;
}
